Discover local Codex plugins as stdio MCP presets - #1837
Merged
Conversation
Contributor
Cloudflare previewTorn down — the PR is closed. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-marketing | 2b40ae6 | Commit Preview URL Branch Preview URL |
Aug 29 2026, 10:21 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-cloud | 2b40ae6 | Aug 29 2026, 10:22 PM |
@executor-js/cli
@executor-js/config
@executor-js/execution
@executor-js/sdk
@executor-js/codemode-core
@executor-js/runtime-quickjs
@executor-js/plugin-file-secrets
@executor-js/plugin-graphql
@executor-js/plugin-keychain
@executor-js/plugin-mcp
@executor-js/plugin-onepassword
@executor-js/plugin-openapi
executor
commit: |
RhysSullivan
marked this pull request as ready for review
August 29, 2026 22:24
This was referenced Aug 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On local builds, the MCP add form's Stdio tab now shows a Codex plugins section: locally installed OpenAI Codex plugins that ship a stdio MCP server (Apple Messages, Computer Use, Computer History, plus anything in the plugin cache with a local-command server), each as a one-click add. Entries whose binaries are missing still render, with an install hint, so the capability is discoverable on machines without Codex. Nothing of OpenAI's is bundled or downloaded — the scanner only reads what the user's own Codex install already put on disk.
codex-plugins.ts: server-side scanner over$CODEX_HOME. The three plugins implemented by the shared Codex Computer Use app are curated and spawn the stable client binary (survives plugin-cache version bumps); other cached plugins are scanned from their manifests, newest version wins.GET /mcp/codex-plugins+ a Codex plugins section inAddMcpIntegration's stdio tab. One click registers the integration with the reported command/args/cwd/env, auto-connects, and syncs tools.discoverToolsnow answerselicitation/createwith a decline during discovery. The connection advertises the capability, and these plugins elicit for first-use approvals; previously the request had no handler on the discovery path.dangerouslyAllowStdioMCP; cloud surfaces return an empty list and never load the scanner (dynamic import, stdio-connector pattern).Testing: unit coverage for the scanner (fixture Codex home: availability, version pick, path resolution, malformed entries) and for the discovery-path decline; e2e scenario
local/codex-plugins.test.tsboots the real local server withCODEX_HOMEpointed at a fixture and drives list → add → auto-connect → tool discovery, including proof thatCODEX_HOMEreached the spawned subprocess. Also verified live against a real Codex install: adding Apple Messages from the UI discovered its 6 tools.Update: the Codex plugins live only in the connect dialog catalog, like every other preset - there is no extra section on the Add MCP screen. Searching "imessage", "texts", or "computer use" finds them; picking one opens a focused single-plugin add screen showing the plugin's own icon (read at runtime from the local Codex install, never shipped), its availability, and one Add action. The catalog metadata shares a module with the scanner so names and summaries cannot drift.
Second update: the focused add screen mirrors the plugin page in Codex - the plugin's own icon, display name, tagline, and long description, all read at runtime from the local install's manifest. Icons render everywhere (connect dialog, command palette, sidebar/integration rows) through a new
executor:icon scheme: local auth is deliberately bearer-header-only, so an img tag cannot authenticate; the shared PresetIcon helper and IntegrationFavicon resolve the scheme by fetching/mcp/codex-plugins/:id/iconwith the auth header and rendering the returned data URI.Third update: curated Codex plugins now call tools through a
codex app-serverbridge. The plugins\x27 service stopped honouring tool calls from non-Codex hosts (direct client spawns can list tools but every call hangs), so the connector now speaks Codex\x27 own app-server protocol: an in-process transport adapter spawnscodex app-server, starts a thread, translates tools/list to mcpServerStatus/list and tools/call to mcpServer/tool/call, and forwards app-server elicitations as standard MCP elicitation/create. No model turn is involved - these are direct tool invocations. The adapter owns its stdio wire because the SDK transport validates incoming lines against MCP schemas and drops app-server traffic. Stored configs stay stdio with an optional appServer marker; the ordinary client, discovery, invoke, health, and elicitation paths are unchanged. Verified live: Messages find_chats returns real data through the executor MCP endpoint in ~3s, and Computer History tools execute. Computer Use itself is currently gated inside Codex (its server is never started for app-server threads in the current build), so its card adds but discovers no tools until Codex re-enables it.Fourth update: the plugins' own approval prompts now bubble up instead of being silently refused. Tools that ask before returning data —
read_messagesabove all — were failing with an unexplained "Read access was not approved", with no prompt anywhere. The cause was the thread the bridge starts: Codex declines every MCP elicitation itself on a thread whose approval policy does not permit them, so the prompt never left Codex. The bridge now starts its thread with an approval policy that routes those prompts to the client, where they arrive as ordinary MCPelicitation/createand executor's existing elicitation bridge handles them. Verified live end to end: reading a chat through the executor MCP endpoint pauses the execution with the plugin's own form ("Once / For this conversation / Always"), and resuming with an accept returns the real messages. The bridge starts no turns, so nothing else can escalate on that thread.Fifth update: two more Codex plugins, and two fixes behind them.
"For this conversation" approvals now hold. Connections were pooled for remote transports only, so every tool call built a fresh connector — and for the bridge that meant a fresh
codex app-serverand a fresh thread. The approval grant lives on the Codex thread, so a session-scoped grant was discarded after every call and re-asked on the next one. App-server bridge connections are now poolable, keyed on their spawn recipe, server, and credentials; plain stdio stays unpooled, since a spawn-per-call CLI server is entitled to a fresh process. Two reads of one chat, approved once with "For this conversation", now prompt once. It also removes a full app-server boot per call.Computer Use works. It is not a broken MCP server — it has no server at all in current Codex. The plugin ships as a
node-replvariant whose API is the bundled@oai/skypackage driven through thenode_replserver'sjstool. Rather than hand an agent a raw JavaScript REPL, the bridge projects that API as eleven typed tools with real input schemas (list_apps,get_app_state,click,type_text, …) and compiles each call into the one REPL program that performs it. The raw REPL is never exposed, so there is no second code-execution tool in the catalog.Chrome, by the same mechanism. The Chrome plugin is skills-only; browser control is its bundled
browser-client.mjs, imported into the same REPL. It is projected as fifteen typed tools (list_tabs,new_tab,navigate,read_page,click,type_text, …). Three things this needed: interaction goes through the tab'sdom_cuaAPI rather thanax, which the plugin's own reference marks unsupported on the extension backend Chrome uses; the client module is resolved through thelatestsymlink Codex maintains, so a plugin update does not strand the stored path; and REPL calls carry synthesized Codex turn metadata, without which the client refuses every call. The plugin's own per-site approval surfaces through the normal elicitation path.OpenAI Developer Docs is a plain server Codex carries itself — search and read the API reference and OpenAPI specs. Curated cards now declare what each one needs on disk, so a machine with Codex but no Computer Use app shows the docs card ready and the others with a setup hint, instead of gating everything on one marker.
Verified live through the executor MCP endpoint: Computer Use lists real apps, Chrome opens a tab and returns its DOM snapshot with node ids, the docs plugin returns real search hits, and Messages reads a chat after one approval. Still no Codex inference on any path —
mcpServer/tool/callruns no model turn, and Codex records no rollout or token usage for these calls.Not included, deliberately:
codex_apps(260 connector tools) overlaps executor's own GitHub, Linear, and Google Calendar integrations and deserves a product decision first. MCP resources and server-change notifications are unimplemented in the bridge and are recorded inDESIRES.md.Sixth update: approval terms, change notifications, and what parity actually means here.
Approval terms now reach the caller. A Codex plugin's prompt can state the terms of the grant rather than just its question — the browser one sends an empty
requestedSchemaand putspersist: "always"and theoriginin its metadata, so accepting means a permanent per-site allow. That metadata was being dropped, leaving a caller consenting to more than the prompt said.FormElicitation/UrlElicitationgained an optional opaquemeta, the MCP boundary populates it, and a paused execution now prints an "Approval terms" block and carries them in its structured payload. The prompt is also attributed to the plugin a person recognises ("Browser use") instead of the server it travelled through (node_repl).Server restarts no longer strand a stale catalog. Codex reports a bridged server's startup transitions; the bridge translates its own server becoming ready into the spec's
notifications/tools/list_changed, which executor already acts on by restaling the connection's catalog. Other servers' transitions are ignored.Two things are deliberately not done, both recorded in
DESIRES.mdwith evidence rather than left implicit. Resources are still unbridged because nothing above the bridge consumes MCP resources yet — adding them today would be a surface with no reader. And approvals persist only for the life of a pooled connection: two calls in one session share a Codex thread and prompt once, but the pool's idle eviction starts a new thread and prompts again. The plugin decides that from its own persisted store, and the app-server elicitation response's_metais dropped insidecodex-app-server-protocolbefore the plugin sees it, so that is not the channel a third-party host can use.Verified live after the change: Computer Use lists apps, Chrome opens a tab and reads its DOM, the docs plugin searches, and Messages reads chats — all returning real data, with approvals prompting once per session and not again.